/*
 * Button.java
 *
 * Created on December 1, 2006, 9:24 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package edu.umn.cs5115.scheduler.framework.gui;

import edu.umn.cs5115.scheduler.framework.IteratorSafeSet;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;

/**
 * A button handles tracking the mouse and redrawing at appropriate times, as
 * well as adding and removing action listeners and sending out notifications
 * when the button is clicked.  You can add images to make the button draw 
 * simple images, or you can override paintComponent and handle drawing yourself
 * based on the button's state.
 * @author grant
 */
public class Button extends FakeWidget
{
    /** Listeners */
    private IteratorSafeSet<ButtonActionListener> listeners = new IteratorSafeSet();
    /** Tracks whether or not the mouse is inside the widget. */
    protected boolean inside;
    /** Tracks whether or not the mouse is pressed. */
    protected boolean mouseIsDown;
    
    /** Images used to draw the button. */
    private ImageSet buttonImages;

    /** Listens for mouse entry/exit events to change the display. */
    private MouseListener mouseListener = new MouseListener()
    {
        public void mouseEntered(MouseEvent e)
        {
            inside = true;
            redraw();
        }
        public void mouseExited(MouseEvent e)
        {
            inside = false;
            redraw();
        }
    };

    /**
     * Add a listener to receive notifications when the button is clicked.
     * @param listener the listener to add.
     */
    public void addActionListener(ButtonActionListener listener)
    {
        listeners.add(listener);
    }

    /**
     * Remove a listener so that it no longer receives notifications of the
     * button being clicked.
     * @param listener the listener to remove.
     */
    public void removeActionListener(ButtonActionListener listener)
    {
        listeners.remove(listener);
    }

    /** Notifies all listeners of the delete action. */
    private void notifyListeners()
    {
        listeners.startIterating();
        for(ButtonActionListener listener : listeners)
            listener.actionPerformed(this);
        listeners.finishIterating();
    }

    /** 
     * Create a new button.
     * If you haven't overriden paintComponent, you'll need to set the image set
     * before the button will be displayable.
     * @see #Button(ImageSet)
     * @see #setImageSet(ImageSet)
     */
    public Button()
    {
        addMouseListener(mouseListener);
    }
    
    /** 
     * Create a new button that uses images to display itself.  The button will
     * automatically resize itself to be the size of the "normal" image.  In 
     * general, all the images should be the same size.
     * @param images The set of images that will be used to draw the button
     * in various states.
     */
    public Button(ImageSet images)
    {
        setImageSet(images);
    }
    
    /** Set the image set used to draw the button.
     * @param imageSet the new set of images to use.
     */
    public void setImageSet(ImageSet imageSet)
    {
        buttonImages = imageSet;
        Rectangle imageBounds = buttonImages.normal.getBounds();
        setSize(imageBounds.width, imageBounds.height);
        redraw();
    }
    
    /**
     * Gets the image set used to draw the button.
     * @return The set of images currently used to draw the button.
     */
    public ImageSet getImageSet()
    {
        return buttonImages;
    }

    public boolean mouseDown(MouseEvent e)
    {
        mouseIsDown = true;
        redraw();
        return true;
    }

    public boolean mouseUp(MouseEvent e)
    {
        mouseIsDown = false;
        if (inside)
            notifyListeners();
        redraw();
        return true;
    }

    /** draw the button. */
    public void paintComponent(GC g)
    {
        if (buttonImages != null)
        {
            if (inside)
            {
                if (mouseIsDown)
                {
                    if (buttonImages.mouseDown != null)
                        g.drawImage(buttonImages.mouseDown, 0, 0);
                } else {
                    if (buttonImages.mouseOver != null)
                        g.drawImage(buttonImages.mouseOver, 0, 0);   
                }
            } else {
                if (buttonImages.normal != null)
                    g.drawImage(buttonImages.normal, 0, 0);
            }
        }
    }

    /** Interface that can receive notifications of when the x is clicked. */
    public static interface ButtonActionListener
    {
        /** Indicates that the button was pressed. 
         * @param button The button that was clicked.
         */
        public void actionPerformed(Button button);
    }
    
    /** Holds the images used by a button to draw it in various states. */
    public static class ImageSet
    {
        /**
         * Initialize the image set with the given three images.
         * @param normal How the button looks normally.
         * @param mouseOver How the button looks when the mouse is over it.
         * @param mouseDown How the button looks when the mouse is over it and pressed.
         */
        public ImageSet(Image normal, Image mouseOver, Image mouseDown)
        {
            this.normal = normal;
            this.mouseOver = mouseOver;
            this.mouseDown = mouseDown;
        }
        
        /** How the button looks normally, when the mouse is not over it. */
        public Image normal;
        /** How the button looks when the mouse is over it, but not pressed. */
        public Image mouseOver;
        /** How the button looks when the mouse is over it and pressed. */
        public Image mouseDown;
    }
}
